1use std::num::NonZeroIsize;
2use std::sync::Mutex;
3
4use crate::common::{ChildKillablePointer, KillablePointer};
5use crate::filter::RgbaPixel;
6use crate::generic::{EditSection, ReadSection};
7
8#[derive(Debug)]
18pub struct EditHandle {
19 pub(crate) internal: *mut aviutl2_sys::plugin2::EDIT_HANDLE,
20 pub(crate) is_ready: std::sync::Arc<std::sync::atomic::AtomicBool>,
21}
22
23unsafe impl Send for EditHandle {}
24unsafe impl Sync for EditHandle {}
25
26#[derive(thiserror::Error, Debug)]
28pub enum EditHandleError {
29 #[error("api call failed")]
30 ApiCallFailed,
31 #[error("effect does not exist")]
32 EffectNotFound,
33 #[error("input utf-16 string contains null byte")]
34 InputCwstrContainsNull(#[from] crate::common::NullByteError),
35 #[error("unknown edit state: {0}")]
36 UnknownEditState(i32),
37 #[error("value is out of range")]
38 ValueOutOfRange,
39}
40
41#[derive(Debug, Clone, Copy)]
43pub struct RenderingSceneVideo<'a> {
44 pub frame: u32,
46 pub width: u32,
48 pub height: u32,
50 pub pitch: u32,
52 pub buffer: &'a [u8],
56}
57
58impl RenderingSceneVideo<'_> {
59 pub fn as_rgba_pixels(&self) -> Option<&[RgbaPixel]> {
63 let expected_pitch = self.width as usize * std::mem::size_of::<RgbaPixel>();
64 if self.pitch as usize != expected_pitch {
65 return None;
66 }
67 Some(unsafe {
68 std::slice::from_raw_parts(
69 self.buffer.as_ptr() as *const RgbaPixel,
70 self.width as usize * self.height as usize,
71 )
72 })
73 }
74}
75
76#[derive(Debug, Clone, Copy)]
78pub struct RenderingSceneAudio<'a> {
79 pub frame: u32,
81 pub buffer0: &'a [f32],
85 pub buffer1: &'a [f32],
89}
90
91impl EditHandle {
92 pub(crate) unsafe fn new(
93 internal: *mut aviutl2_sys::plugin2::EDIT_HANDLE,
94 is_ready: std::sync::Arc<std::sync::atomic::AtomicBool>,
95 ) -> Self {
96 Self { internal, is_ready }
97 }
98
99 pub fn is_ready(&self) -> bool {
101 self.is_ready.load(std::sync::atomic::Ordering::Acquire)
102 }
103
104 pub fn call_edit_section<'a, T, F>(&self, callback: F) -> Result<T, EditHandleError>
110 where
111 T: Send + 'static,
112 F: FnOnce(&mut EditSection) -> T + Send + 'a,
113 {
114 assert!(
115 self.is_ready(),
116 "call_edit_section cannot be called before register_plugin is done"
117 );
118
119 type CallbackParam<'a, F, T> = (ChildKillablePointer<Option<F>>, &'a mut Option<T>);
120
121 let closure = Some(callback);
122 let param = KillablePointer::new(closure);
123 let child_param = param.create_child();
124
125 extern "C" fn trampoline<F, T>(
126 param: *mut std::ffi::c_void,
127 edit_section: *mut aviutl2_sys::plugin2::EDIT_SECTION,
128 ) where
129 T: Send + 'static,
130 F: FnOnce(&mut EditSection) -> T,
131 {
132 unsafe {
133 let (child_param, result_ptr) = &mut *(param as *mut CallbackParam<F, T>);
134 let callback = child_param.as_mut().take().expect("Callback already taken");
135 let mut edit_section = EditSection::from_raw(edit_section);
136 let res = callback(&mut edit_section);
137 result_ptr.replace(res);
138 }
139 }
140
141 let trampoline_static = trampoline::<F, T>
142 as extern "C" fn(*mut std::ffi::c_void, *mut aviutl2_sys::plugin2::EDIT_SECTION);
143
144 let mut result = None;
145 let param = Box::<CallbackParam<F, T>>::new((child_param, &mut result));
146 let param_ptr = Box::into_raw(param);
147
148 let success = unsafe {
149 ((*self.internal).call_edit_section_param)(
150 param_ptr as *mut std::ffi::c_void,
151 trampoline_static,
152 )
153 };
154
155 drop(unsafe { Box::from_raw(param_ptr) });
156
157 if success {
158 Ok(result.expect("Callback did not set result"))
159 } else {
160 Err(EditHandleError::ApiCallFailed)
161 }
162 }
163
164 pub fn call_read_section<'a, T, F>(&self, callback: F) -> Result<T, EditHandleError>
170 where
171 T: Send + 'static,
172 F: FnOnce(&ReadSection) -> T + Send + 'a,
173 {
174 assert!(
175 self.is_ready(),
176 "call_read_section cannot be called before register_plugin is done"
177 );
178
179 type CallbackParam<'a, F, T> = (ChildKillablePointer<Option<F>>, &'a mut Option<T>);
180
181 let closure = Some(callback);
182 let param = KillablePointer::new(closure);
183 let child_param = param.create_child();
184
185 extern "C" fn trampoline<F, T>(
186 param: *mut std::ffi::c_void,
187 read_section: *mut aviutl2_sys::plugin2::EDIT_SECTION,
188 ) where
189 T: Send + 'static,
190 F: FnOnce(&ReadSection) -> T,
191 {
192 unsafe {
193 let (child_param, result_ptr) = &mut *(param as *mut CallbackParam<F, T>);
194 let callback = child_param.as_mut().take().expect("Callback already taken");
195 let read_section = ReadSection::from_raw(read_section);
196 let res = callback(&read_section);
197 result_ptr.replace(res);
198 }
199 }
200
201 let trampoline_static = trampoline::<F, T>
202 as extern "C" fn(*mut std::ffi::c_void, *mut aviutl2_sys::plugin2::EDIT_SECTION);
203
204 let mut result = None;
205 let param = Box::<CallbackParam<F, T>>::new((child_param, &mut result));
206 let param_ptr = Box::into_raw(param);
207
208 let success = unsafe {
209 ((*self.internal).call_read_section_param)(
210 param_ptr as *mut std::ffi::c_void,
211 trampoline_static,
212 )
213 };
214
215 drop(unsafe { Box::from_raw(param_ptr) });
216
217 if success {
218 Ok(result.expect("Callback did not set result"))
219 } else {
220 Err(EditHandleError::ApiCallFailed)
221 }
222 }
223
224 pub fn get_edit_info(&self) -> crate::generic::EditInfo {
226 assert!(
227 self.is_ready(),
228 "get_edit_info cannot be called before register_plugin is done"
229 );
230 let mut raw_info = std::mem::MaybeUninit::<aviutl2_sys::plugin2::EDIT_INFO>::uninit();
231 unsafe {
232 ((*self.internal).get_edit_info)(
233 raw_info.as_mut_ptr(),
234 std::mem::size_of::<aviutl2_sys::plugin2::EDIT_INFO>() as _,
235 );
236 let edit_info = raw_info.assume_init();
237 crate::generic::EditInfo::from_raw(&edit_info)
238 }
239 }
240
241 pub fn restart_host_app(&self) {
243 assert!(
244 self.is_ready(),
245 "restart_host_app cannot be called before register_plugin is done"
246 );
247 unsafe {
248 ((*self.internal).restart_host_app)();
249 }
250 }
251
252 pub fn enumerate_effects<F>(&self, callback: F)
258 where
259 F: FnMut(Effect),
260 {
261 assert!(
262 self.is_ready(),
263 "enumerate_effects cannot be called before register_plugin is done"
264 );
265 type CallbackParam<F> = ChildKillablePointer<F>;
266
267 extern "C" fn trampoline<F>(
268 param: *mut std::ffi::c_void,
269 name: aviutl2_sys::common::LPCWSTR,
270 r#type: i32,
271 flag: i32,
272 ) where
273 F: FnMut(Effect),
274 {
275 let callback = unsafe { &mut *(param as *mut CallbackParam<F>) };
276 let callback = unsafe { callback.as_mut() };
277 let name_str = unsafe { crate::common::load_wide_string(name) };
278 if let Ok(effect_type) = EffectType::try_from(r#type) {
279 let effect = Effect {
280 name: name_str,
281 effect_type,
282 flag: EffectFlag::from_bits(flag),
283 };
284 callback(effect);
285 } else {
286 #[cfg(debug_assertions)]
287 tracing::warn!("Unknown effect type: {}", r#type);
288 }
289 }
290
291 let trampoline_static = trampoline::<F>
292 as extern "C" fn(*mut std::ffi::c_void, aviutl2_sys::common::LPCWSTR, i32, i32);
293 let callback_guard = KillablePointer::new(callback);
294 let child_param = callback_guard.create_child();
295 let param = Box::new(child_param);
296 let param_ptr = Box::into_raw(param);
297 unsafe {
298 ((*self.internal).enum_effect_name)(
299 param_ptr as *mut std::ffi::c_void,
300 trampoline_static,
301 );
302 }
303 drop(unsafe { Box::from_raw(param_ptr) });
304 }
305
306 pub fn get_effects(&self) -> Vec<Effect> {
308 assert!(
309 self.is_ready(),
310 "get_effects cannot be called before register_plugin is done"
311 );
312 let mut effects = Vec::new();
313 self.enumerate_effects(|effect| {
314 effects.push(effect);
315 });
316 effects
317 }
318
319 pub fn enumerate_effect_items<F>(
329 &self,
330 effect: &str,
331 callback: F,
332 ) -> Result<(), EditHandleError>
333 where
334 F: FnMut(EffectItemInfo),
335 {
336 assert!(
337 self.is_ready(),
338 "enumerate_effect_items cannot be called before register_plugin is done"
339 );
340 type CallbackParam<F> = ChildKillablePointer<F>;
341
342 unsafe extern "C" fn trampoline<F>(
343 param: *mut std::ffi::c_void,
344 name: aviutl2_sys::common::LPCWSTR,
345 r#type: i32,
346 ) where
347 F: FnMut(EffectItemInfo),
348 {
349 let callback = unsafe { &mut *(param as *mut CallbackParam<F>) };
350 let callback = unsafe { callback.as_mut() };
351 let name = unsafe { crate::common::load_wide_string(name) };
352 if let Some(info) = effect_item_info_from_raw(name, r#type) {
353 callback(info);
354 }
355 }
356
357 let effect = crate::common::CWString::new(effect)?;
358 let trampoline_static = trampoline::<F>
359 as unsafe extern "C" fn(*mut std::ffi::c_void, aviutl2_sys::common::LPCWSTR, i32);
360 let callback_guard = KillablePointer::new(callback);
361 let child_param = callback_guard.create_child();
362 let param = Box::new(child_param);
363 let param_ptr = Box::into_raw(param);
364 let success = unsafe {
365 ((*self.internal).enum_effect_item)(
366 effect.as_ptr(),
367 param_ptr as *mut std::ffi::c_void,
368 trampoline_static,
369 )
370 };
371 drop(unsafe { Box::from_raw(param_ptr) });
372
373 if success {
374 Ok(())
375 } else {
376 Err(EditHandleError::EffectNotFound)
377 }
378 }
379
380 pub fn get_effect_items(&self, effect: &str) -> Result<Vec<EffectItemInfo>, EditHandleError> {
386 assert!(
387 self.is_ready(),
388 "get_effect_items cannot be called before register_plugin is done"
389 );
390 let mut items = Vec::new();
391 self.enumerate_effect_items(effect, |item| {
392 items.push(item);
393 })?;
394 Ok(items)
395 }
396
397 pub fn get_effect_item_group_index(
399 &self,
400 effect: &str,
401 item_name: &str,
402 ) -> Result<Option<usize>, EditHandleError> {
403 assert!(
404 self.is_ready(),
405 "get_effect_item_group_index cannot be called before register_plugin is done"
406 );
407 let effect = crate::common::CWString::new(effect)?;
408 let item_name = crate::common::CWString::new(item_name)?;
409 let item_count = unsafe {
410 ((*self.internal).get_effect_item_group_names)(
411 effect.as_ptr(),
412 item_name.as_ptr(),
413 std::ptr::null_mut(),
414 0,
415 std::ptr::null_mut(),
416 )
417 };
418 if item_count <= 0 {
419 return Ok(None);
420 }
421
422 let mut names: Vec<*const u16> = vec![std::ptr::null(); item_count as usize];
423 let mut index: i32 = -1;
424 unsafe {
425 ((*self.internal).get_effect_item_group_names)(
426 effect.as_ptr(),
427 item_name.as_ptr(),
428 names.as_mut_ptr(),
429 item_count,
430 &mut index as *mut i32,
431 )
432 };
433 if index < 0 {
434 return Err(EditHandleError::ApiCallFailed);
435 }
436 Ok(Some(index as usize))
437 }
438
439 pub fn get_effect_item_group_names(
441 &self,
442 effect: &str,
443 item_name: &str,
444 ) -> Result<Option<Vec<String>>, EditHandleError> {
445 assert!(
446 self.is_ready(),
447 "get_effect_item_group_names cannot be called before register_plugin is done"
448 );
449 let effect = crate::common::CWString::new(effect)?;
450 let item_name = crate::common::CWString::new(item_name)?;
451 let item_count = unsafe {
452 ((*self.internal).get_effect_item_group_names)(
453 effect.as_ptr(),
454 item_name.as_ptr(),
455 std::ptr::null_mut(),
456 0,
457 std::ptr::null_mut(),
458 )
459 };
460 if item_count <= 0 {
461 return Ok(None);
462 }
463
464 let mut names: Vec<*const u16> = vec![std::ptr::null(); item_count as usize];
465 let actual_item_count = unsafe {
466 ((*self.internal).get_effect_item_group_names)(
467 effect.as_ptr(),
468 item_name.as_ptr(),
469 names.as_mut_ptr(),
470 item_count,
471 std::ptr::null_mut(),
472 )
473 };
474
475 if actual_item_count != item_count {
476 return Err(EditHandleError::ApiCallFailed);
477 }
478
479 let names: Vec<String> = names
480 .into_iter()
481 .map(|ptr| unsafe { crate::common::load_wide_string(ptr) })
482 .collect();
483
484 Ok(Some(names))
485 }
486
487 pub fn enumerate_modules<F>(&self, callback: F)
489 where
490 F: FnMut(ModuleInfo),
491 {
492 assert!(
493 self.is_ready(),
494 "enumerate_modules cannot be called before register_plugin is done"
495 );
496 type CallbackParam<F> = ChildKillablePointer<F>;
497
498 extern "C" fn trampoline<F>(
499 param: *mut std::ffi::c_void,
500 module: *mut aviutl2_sys::plugin2::MODULE_INFO,
501 ) where
502 F: FnMut(ModuleInfo),
503 {
504 let callback = unsafe { &mut *(param as *mut CallbackParam<F>) };
505 let callback = unsafe { callback.as_mut() };
506 if let Some(module_info) = module_info_from_raw(module) {
507 callback(module_info);
508 }
509 }
510 let trampoline_static = trampoline::<F>
511 as unsafe extern "C" fn(*mut std::ffi::c_void, *mut aviutl2_sys::plugin2::MODULE_INFO);
512 let callback_guard = KillablePointer::new(callback);
513 let child_param = callback_guard.create_child();
514 let param = Box::new(child_param);
515 let param_ptr = Box::into_raw(param);
516 unsafe {
517 ((*self.internal).enum_module_info)(
518 param_ptr as *mut std::ffi::c_void,
519 trampoline_static,
520 );
521 }
522 drop(unsafe { Box::from_raw(param_ptr) });
523 }
524
525 pub fn get_modules(&self) -> Vec<ModuleInfo> {
527 assert!(
528 self.is_ready(),
529 "get_modules cannot be called before register_plugin is done"
530 );
531 let mut modules = Vec::new();
532 self.enumerate_modules(|module| {
533 modules.push(module);
534 });
535 modules
536 }
537
538 pub fn get_host_app_window_raw(&self) -> Option<raw_window_handle::Win32WindowHandle> {
540 let hwnd = unsafe { ((*self.internal).get_host_app_window)() };
541 NonZeroIsize::new(hwnd as isize).map(raw_window_handle::Win32WindowHandle::new)
542 }
543
544 pub unsafe fn get_host_app_window(&'_ self) -> Option<raw_window_handle::WindowHandle<'_>> {
550 self.get_host_app_window_raw().map(|handle| unsafe {
551 raw_window_handle::WindowHandle::borrow_raw(raw_window_handle::RawWindowHandle::Win32(
552 handle,
553 ))
554 })
555 }
556
557 pub fn get_edit_state(&self) -> Result<EditState, EditHandleError> {
559 assert!(
560 self.is_ready(),
561 "get_edit_state cannot be called before register_plugin is done"
562 );
563 let state = unsafe { ((*self.internal).get_edit_state)() };
564 EditState::try_from(state).map_err(|_| EditHandleError::UnknownEditState(state))
565 }
566
567 pub fn rendering_scene_video<F>(&self, frame: u32, callback: F) -> Result<(), EditHandleError>
572 where
573 F: FnMut(RenderingSceneVideo<'_>) + Send + 'static,
574 {
575 assert!(
576 self.is_ready(),
577 "rendering_scene_video cannot be called before register_plugin is done"
578 );
579
580 type CallbackParam<F> = Mutex<Option<F>>;
581
582 unsafe extern "C" fn trampoline<F>(
583 param: *mut std::ffi::c_void,
584 frame: i32,
585 buffer: *const std::ffi::c_void,
586 width: i32,
587 height: i32,
588 pitch: i32,
589 ) where
590 F: FnMut(RenderingSceneVideo<'_>),
591 {
592 let callback = unsafe { Box::from_raw(param as *mut CallbackParam<F>) };
593 let len = usize::try_from(pitch)
594 .ok()
595 .and_then(|pitch| {
596 usize::try_from(height)
597 .ok()
598 .and_then(|height| pitch.checked_mul(height))
599 })
600 .unwrap_or(0);
601 let buffer = if buffer.is_null() || len == 0 {
602 &[]
603 } else {
604 unsafe { std::slice::from_raw_parts(buffer as *const u8, len) }
605 };
606 let video = RenderingSceneVideo {
607 frame: frame as u32,
608 width: width as u32,
609 height: height as u32,
610 pitch: pitch as u32,
611 buffer,
612 };
613 let mut callback = callback
614 .lock()
615 .unwrap()
616 .take()
617 .expect("Callback already taken");
618 callback(video);
619 }
620
621 let frame = i32::try_from(frame).map_err(|_| EditHandleError::ValueOutOfRange)?;
622 let param = Box::into_raw(Box::new(Mutex::new(Some(callback))));
623 let success = unsafe {
624 ((*self.internal).rendering_scene_video)(
625 frame,
626 param as *mut std::ffi::c_void,
627 trampoline::<F>,
628 )
629 };
630 if success {
631 Ok(())
632 } else {
633 unsafe {
634 drop(Box::from_raw(param));
635 }
636 Err(EditHandleError::ApiCallFailed)
637 }
638 }
639
640 pub fn rendering_scene_audio<F>(&self, frame: u32, callback: F) -> Result<(), EditHandleError>
645 where
646 F: FnMut(RenderingSceneAudio<'_>) + Send + 'static,
647 {
648 assert!(
649 self.is_ready(),
650 "rendering_scene_audio cannot be called before register_plugin is done"
651 );
652
653 type CallbackParam<F> = Mutex<Option<F>>;
654
655 unsafe extern "C" fn trampoline<F>(
656 param: *mut std::ffi::c_void,
657 frame: i32,
658 buffer0: *const f32,
659 buffer1: *const f32,
660 sample_num: i32,
661 ) where
662 F: FnMut(RenderingSceneAudio<'_>),
663 {
664 let callback = unsafe { Box::from_raw(param as *mut CallbackParam<F>) };
665 let len = usize::try_from(sample_num).unwrap_or(0);
666 let buffer0 = if buffer0.is_null() || len == 0 {
667 &[]
668 } else {
669 unsafe { std::slice::from_raw_parts(buffer0, len) }
670 };
671 let buffer1 = if buffer1.is_null() || len == 0 {
672 &[]
673 } else {
674 unsafe { std::slice::from_raw_parts(buffer1, len) }
675 };
676 let audio = RenderingSceneAudio {
677 frame: frame as u32,
678 buffer0,
679 buffer1,
680 };
681 let mut callback = callback
682 .lock()
683 .unwrap()
684 .take()
685 .expect("Callback already taken");
686 callback(audio);
687 }
688
689 let frame = i32::try_from(frame).map_err(|_| EditHandleError::ValueOutOfRange)?;
690 let param = Box::into_raw(Box::new(Mutex::new(Some(callback))));
691 let success = unsafe {
692 ((*self.internal).rendering_scene_audio)(
693 frame,
694 param as *mut std::ffi::c_void,
695 trampoline::<F>,
696 )
697 };
698 if success {
699 Ok(())
700 } else {
701 unsafe {
702 drop(Box::from_raw(param));
703 }
704 Err(EditHandleError::ApiCallFailed)
705 }
706 }
707
708 pub fn rendering_object_video<F>(
719 &self,
720 object: crate::generic::ObjectHandle,
721 frame: u32,
722 apply_effect: bool,
723 callback: F,
724 ) -> Result<(), EditHandleError>
725 where
726 F: FnMut(RenderingSceneVideo<'_>) + Send + 'static,
727 {
728 assert!(
729 self.is_ready(),
730 "rendering_object_video cannot be called before register_plugin is done"
731 );
732
733 type CallbackParam<F> = Mutex<Option<F>>;
734
735 unsafe extern "C" fn trampoline<F>(
736 param: *mut std::ffi::c_void,
737 frame: i32,
738 buffer: *const std::ffi::c_void,
739 width: i32,
740 height: i32,
741 pitch: i32,
742 ) where
743 F: FnMut(RenderingSceneVideo<'_>),
744 {
745 let callback = unsafe { Box::from_raw(param as *mut CallbackParam<F>) };
746 let len = usize::try_from(pitch)
747 .ok()
748 .and_then(|pitch| {
749 usize::try_from(height)
750 .ok()
751 .and_then(|height| pitch.checked_mul(height))
752 })
753 .unwrap_or(0);
754 let buffer = if buffer.is_null() || len == 0 {
755 &[]
756 } else {
757 unsafe { std::slice::from_raw_parts(buffer as *const u8, len) }
758 };
759 let video = RenderingSceneVideo {
760 frame: frame as u32,
761 width: width as u32,
762 height: height as u32,
763 pitch: pitch as u32,
764 buffer,
765 };
766 let mut callback = callback
767 .lock()
768 .unwrap()
769 .take()
770 .expect("Callback already taken");
771 callback(video);
772 }
773
774 let frame = i32::try_from(frame).map_err(|_| EditHandleError::ValueOutOfRange)?;
775 let param = Box::into_raw(Box::new(Mutex::new(Some(callback))));
776 let success = unsafe {
777 ((*self.internal).rendering_object_video)(
778 object.internal,
779 frame,
780 apply_effect as _,
781 param as *mut std::ffi::c_void,
782 trampoline::<F>,
783 )
784 };
785 if success {
786 Ok(())
787 } else {
788 unsafe {
789 drop(Box::from_raw(param));
790 }
791 Err(EditHandleError::ApiCallFailed)
792 }
793 }
794
795 pub fn rendering_object_audio<F>(
806 &self,
807 object: crate::generic::ObjectHandle,
808 frame: u32,
809 apply_effect: bool,
810 callback: F,
811 ) -> Result<(), EditHandleError>
812 where
813 F: FnMut(RenderingSceneAudio<'_>) + Send + 'static,
814 {
815 assert!(
816 self.is_ready(),
817 "rendering_object_audio cannot be called before register_plugin is done"
818 );
819
820 type CallbackParam<F> = Mutex<Option<F>>;
821
822 unsafe extern "C" fn trampoline<F>(
823 param: *mut std::ffi::c_void,
824 frame: i32,
825 buffer0: *const f32,
826 buffer1: *const f32,
827 sample_num: i32,
828 ) where
829 F: FnMut(RenderingSceneAudio<'_>),
830 {
831 let callback = unsafe { Box::from_raw(param as *mut CallbackParam<F>) };
832 let len = usize::try_from(sample_num).unwrap_or(0);
833 let buffer0 = if buffer0.is_null() || len == 0 {
834 &[]
835 } else {
836 unsafe { std::slice::from_raw_parts(buffer0, len) }
837 };
838 let buffer1 = if buffer1.is_null() || len == 0 {
839 &[]
840 } else {
841 unsafe { std::slice::from_raw_parts(buffer1, len) }
842 };
843 let audio = RenderingSceneAudio {
844 frame: frame as u32,
845 buffer0,
846 buffer1,
847 };
848 let mut callback = callback
849 .lock()
850 .unwrap()
851 .take()
852 .expect("Callback already taken");
853 callback(audio);
854 }
855
856 let frame = i32::try_from(frame).map_err(|_| EditHandleError::ValueOutOfRange)?;
857 let param = Box::into_raw(Box::new(Mutex::new(Some(callback))));
858 let success = unsafe {
859 ((*self.internal).rendering_object_audio)(
860 object.internal,
861 frame,
862 apply_effect as _,
863 param as *mut std::ffi::c_void,
864 trampoline::<F>,
865 )
866 };
867 if success {
868 Ok(())
869 } else {
870 unsafe {
871 drop(Box::from_raw(param));
872 }
873 Err(EditHandleError::ApiCallFailed)
874 }
875 }
876
877 pub fn wait_rendering_task(&self) {
887 assert!(
888 self.is_ready(),
889 "wait_rendering_task cannot be called before register_plugin is done"
890 );
891 unsafe {
892 ((*self.internal).wait_rendering_task)();
893 }
894 }
895
896 pub fn enumerate_font_names<F>(&self, callback: F)
898 where
899 F: FnMut(String),
900 {
901 assert!(
902 self.is_ready(),
903 "enumerate_font_names cannot be called before register_plugin is done"
904 );
905 type CallbackParam<F> = ChildKillablePointer<F>;
906
907 unsafe extern "C" fn trampoline<F>(
908 param: *mut std::ffi::c_void,
909 name: aviutl2_sys::common::LPCWSTR,
910 ) where
911 F: FnMut(String),
912 {
913 let callback = unsafe { &mut *(param as *mut CallbackParam<F>) };
914 let callback = unsafe { callback.as_mut() };
915 let name_str = unsafe { crate::common::load_wide_string(name) };
916 callback(name_str);
917 }
918
919 let trampoline_static = trampoline::<F>
920 as unsafe extern "C" fn(*mut std::ffi::c_void, aviutl2_sys::common::LPCWSTR);
921 let callback_guard = KillablePointer::new(callback);
922 let child_param = callback_guard.create_child();
923 let param = Box::new(child_param);
924 let param_ptr = Box::into_raw(param);
925 unsafe {
926 ((*self.internal).enum_font_name)(
927 param_ptr as *mut std::ffi::c_void,
928 trampoline_static,
929 );
930 }
931 drop(unsafe { Box::from_raw(param_ptr) });
932 }
933
934 pub fn get_font_names(&self) -> Vec<String> {
936 assert!(
937 self.is_ready(),
938 "get_font_names cannot be called before register_plugin is done"
939 );
940 let mut font_names = Vec::new();
941 self.enumerate_font_names(|name| {
942 font_names.push(name);
943 });
944 font_names
945 }
946
947 pub fn enumerate_palette_names<F>(&self, callback: F)
949 where
950 F: FnMut(String),
951 {
952 assert!(
953 self.is_ready(),
954 "enumerate_palette_names cannot be called before register_plugin is done"
955 );
956 type CallbackParam<F> = ChildKillablePointer<F>;
957
958 unsafe extern "C" fn trampoline<F>(
959 param: *mut std::ffi::c_void,
960 name: aviutl2_sys::common::LPCWSTR,
961 ) where
962 F: FnMut(String),
963 {
964 let callback = unsafe { &mut *(param as *mut CallbackParam<F>) };
965 let callback = unsafe { callback.as_mut() };
966 let name_str = unsafe { crate::common::load_wide_string(name) };
967 callback(name_str);
968 }
969
970 let trampoline_static = trampoline::<F>
971 as unsafe extern "C" fn(*mut std::ffi::c_void, aviutl2_sys::common::LPCWSTR);
972 let callback_guard = KillablePointer::new(callback);
973 let child_param = callback_guard.create_child();
974 let param = Box::new(child_param);
975 let param_ptr = Box::into_raw(param);
976 unsafe {
977 ((*self.internal).enum_palette_name)(
978 param_ptr as *mut std::ffi::c_void,
979 trampoline_static,
980 );
981 }
982 drop(unsafe { Box::from_raw(param_ptr) });
983 }
984
985 pub fn get_palette_names(&self) -> Vec<String> {
987 assert!(
988 self.is_ready(),
989 "get_palette_names cannot be called before register_plugin is done"
990 );
991 let mut palette_names = Vec::new();
992 self.enumerate_palette_names(|name| {
993 palette_names.push(name);
994 });
995 palette_names
996 }
997}
998
999#[derive(Debug, Clone, PartialEq, Eq)]
1001pub struct Effect {
1002 pub name: String,
1004 pub effect_type: EffectType,
1006 pub flag: EffectFlag,
1008}
1009
1010#[derive(Debug, Clone, PartialEq, Eq)]
1012pub struct EffectItemInfo {
1013 pub name: String,
1015 pub item_type: EffectItemType,
1017}
1018
1019#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1021pub enum EffectType {
1022 Filter,
1024 Input,
1026 SceneChange,
1028 Control,
1030 Output,
1032}
1033
1034#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1036pub enum EffectItemType {
1037 Integer,
1039 Number,
1041 Check,
1043 Text,
1045 String,
1047 File,
1049 Color,
1051 Select,
1053 Scene,
1055 Range,
1057 Combo,
1059 Mask,
1061 Font,
1063 Figure,
1065 Data,
1067 Folder,
1069 NumberGroup,
1071 Group,
1073 Separator,
1075}
1076
1077define_bitflag! {
1078 #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)]
1080 #[non_exhaustive]
1081 pub struct EffectFlag: i32 {
1082 video: aviutl2_sys::plugin2::EDIT_HANDLE::EFFECT_FLAG_VIDEO,
1084
1085 audio: aviutl2_sys::plugin2::EDIT_HANDLE::EFFECT_FLAG_AUDIO,
1087
1088 as_filter: aviutl2_sys::plugin2::EDIT_HANDLE::EFFECT_FLAG_FILTER,
1090
1091 camera: aviutl2_sys::plugin2::EDIT_HANDLE::EFFECT_FLAG_CAMERA,
1093 }
1094}
1095
1096impl TryFrom<i32> for EffectType {
1097 type Error = ();
1098
1099 fn try_from(value: i32) -> Result<Self, Self::Error> {
1100 match value {
1101 1 => Ok(EffectType::Filter),
1102 2 => Ok(EffectType::Input),
1103 3 => Ok(EffectType::SceneChange),
1104 4 => Ok(EffectType::Control),
1105 5 => Ok(EffectType::Output),
1106 _ => Err(()),
1107 }
1108 }
1109}
1110impl From<EffectType> for i32 {
1111 fn from(value: EffectType) -> Self {
1112 match value {
1113 EffectType::Filter => 1,
1114 EffectType::Input => 2,
1115 EffectType::SceneChange => 3,
1116 EffectType::Control => 4,
1117 EffectType::Output => 5,
1118 }
1119 }
1120}
1121
1122impl TryFrom<i32> for EffectItemType {
1123 type Error = ();
1124
1125 fn try_from(value: i32) -> Result<Self, Self::Error> {
1126 match value {
1127 1 => Ok(EffectItemType::Integer),
1128 2 => Ok(EffectItemType::Number),
1129 3 => Ok(EffectItemType::Check),
1130 4 => Ok(EffectItemType::Text),
1131 5 => Ok(EffectItemType::String),
1132 6 => Ok(EffectItemType::File),
1133 7 => Ok(EffectItemType::Color),
1134 8 => Ok(EffectItemType::Select),
1135 9 => Ok(EffectItemType::Scene),
1136 10 => Ok(EffectItemType::Range),
1137 11 => Ok(EffectItemType::Combo),
1138 12 => Ok(EffectItemType::Mask),
1139 13 => Ok(EffectItemType::Font),
1140 14 => Ok(EffectItemType::Figure),
1141 15 => Ok(EffectItemType::Data),
1142 16 => Ok(EffectItemType::Folder),
1143 17 => Ok(EffectItemType::NumberGroup),
1144 18 => Ok(EffectItemType::Group),
1145 19 => Ok(EffectItemType::Separator),
1146 _ => Err(()),
1147 }
1148 }
1149}
1150impl From<EffectItemType> for i32 {
1151 fn from(value: EffectItemType) -> Self {
1152 match value {
1153 EffectItemType::Integer => 1,
1154 EffectItemType::Number => 2,
1155 EffectItemType::Check => 3,
1156 EffectItemType::Text => 4,
1157 EffectItemType::String => 5,
1158 EffectItemType::File => 6,
1159 EffectItemType::Color => 7,
1160 EffectItemType::Select => 8,
1161 EffectItemType::Scene => 9,
1162 EffectItemType::Range => 10,
1163 EffectItemType::Combo => 11,
1164 EffectItemType::Mask => 12,
1165 EffectItemType::Font => 13,
1166 EffectItemType::Figure => 14,
1167 EffectItemType::Data => 15,
1168 EffectItemType::Folder => 16,
1169 EffectItemType::NumberGroup => 17,
1170 EffectItemType::Group => 18,
1171 EffectItemType::Separator => 19,
1172 }
1173 }
1174}
1175
1176#[derive(Debug, Clone, PartialEq, Eq)]
1178pub struct ModuleInfo {
1179 pub module_type: ModuleType,
1181 pub name: String,
1183 pub information: String,
1185}
1186
1187#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1189pub enum ModuleType {
1190 ScriptFilter,
1192 ScriptObject,
1194 ScriptCamera,
1196 ScriptTrack,
1198 ScriptModule,
1200 PluginInput,
1202 PluginOutput,
1204 PluginFilter,
1206 PluginGeneric,
1208}
1209
1210impl TryFrom<i32> for ModuleType {
1211 type Error = ();
1212
1213 fn try_from(value: i32) -> Result<Self, Self::Error> {
1214 match value {
1215 1 => Ok(ModuleType::ScriptFilter),
1216 2 => Ok(ModuleType::ScriptObject),
1217 3 => Ok(ModuleType::ScriptCamera),
1218 4 => Ok(ModuleType::ScriptTrack),
1219 5 => Ok(ModuleType::ScriptModule),
1220 6 => Ok(ModuleType::PluginInput),
1221 7 => Ok(ModuleType::PluginOutput),
1222 8 => Ok(ModuleType::PluginFilter),
1223 9 => Ok(ModuleType::PluginGeneric),
1224 _ => Err(()),
1225 }
1226 }
1227}
1228impl From<ModuleType> for i32 {
1229 fn from(value: ModuleType) -> Self {
1230 match value {
1231 ModuleType::ScriptFilter => 1,
1232 ModuleType::ScriptObject => 2,
1233 ModuleType::ScriptCamera => 3,
1234 ModuleType::ScriptTrack => 4,
1235 ModuleType::ScriptModule => 5,
1236 ModuleType::PluginInput => 6,
1237 ModuleType::PluginOutput => 7,
1238 ModuleType::PluginFilter => 8,
1239 ModuleType::PluginGeneric => 9,
1240 }
1241 }
1242}
1243
1244#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1246pub enum EditState {
1247 Edit,
1249 Preview,
1251 Save,
1253}
1254
1255impl TryFrom<i32> for EditState {
1256 type Error = ();
1257
1258 fn try_from(value: i32) -> Result<Self, Self::Error> {
1259 match value {
1260 0 => Ok(EditState::Edit),
1261 1 => Ok(EditState::Preview),
1262 2 => Ok(EditState::Save),
1263 _ => Err(()),
1264 }
1265 }
1266}
1267impl From<EditState> for i32 {
1268 fn from(value: EditState) -> Self {
1269 match value {
1270 EditState::Edit => 0,
1271 EditState::Preview => 1,
1272 EditState::Save => 2,
1273 }
1274 }
1275}
1276
1277#[derive(Debug)]
1281pub struct GlobalEditHandle {
1282 edit_handle: std::sync::OnceLock<crate::generic::EditHandle>,
1283}
1284
1285impl GlobalEditHandle {
1286 pub const fn new() -> Self {
1288 Self {
1289 edit_handle: std::sync::OnceLock::new(),
1290 }
1291 }
1292
1293 pub fn init(&self, edit_handle: crate::generic::EditHandle) {
1295 let _ = self
1296 .edit_handle
1297 .set(edit_handle)
1298 .map_err(|_| tracing::warn!("GlobalEditHandle was already initialized"));
1299 }
1300
1301 pub fn is_ready(&self) -> bool {
1303 self.edit_handle
1304 .get()
1305 .is_some_and(|handle| handle.is_ready())
1306 }
1307}
1308
1309impl Default for GlobalEditHandle {
1310 fn default() -> Self {
1311 Self::new()
1312 }
1313}
1314
1315impl std::ops::Deref for GlobalEditHandle {
1316 type Target = crate::generic::EditHandle;
1317
1318 fn deref(&self) -> &Self::Target {
1319 self.edit_handle
1320 .get()
1321 .expect("GlobalEditHandle is not initialized")
1322 }
1323}
1324
1325fn effect_item_info_from_raw(name: String, item_type: i32) -> Option<EffectItemInfo> {
1326 if let Ok(item_type) = EffectItemType::try_from(item_type) {
1327 Some(EffectItemInfo { name, item_type })
1328 } else {
1329 tracing::warn!("Unknown effect item type: {}", item_type);
1330 None
1331 }
1332}
1333
1334fn module_info_from_raw(raw: *mut aviutl2_sys::plugin2::MODULE_INFO) -> Option<ModuleInfo> {
1335 let module_type = unsafe { (*raw).r#type };
1336 if let Ok(module_type) = ModuleType::try_from(module_type) {
1337 Some(ModuleInfo {
1338 module_type,
1339 name: unsafe { crate::common::load_wide_string((*raw).name) },
1340 information: unsafe { crate::common::load_wide_string((*raw).information) },
1341 })
1342 } else {
1343 tracing::warn!("Unknown module type: {}", module_type);
1344 None
1345 }
1346}
1347
1348#[cfg(test)]
1349mod tests {
1350 use super::*;
1351
1352 #[test]
1353 fn effect_item_type_try_from_known_values() {
1354 assert_eq!(EffectItemType::try_from(1), Ok(EffectItemType::Integer));
1355 assert_eq!(EffectItemType::try_from(8), Ok(EffectItemType::Select));
1356 assert_eq!(EffectItemType::try_from(16), Ok(EffectItemType::Folder));
1357 }
1358
1359 #[test]
1360 fn effect_item_type_try_from_unknown_value_fails() {
1361 assert_eq!(EffectItemType::try_from(999), Err(()));
1362 }
1363
1364 #[test]
1365 fn effect_item_type_into_i32() {
1366 assert_eq!(i32::from(EffectItemType::Integer), 1);
1367 assert_eq!(i32::from(EffectItemType::Combo), 11);
1368 assert_eq!(i32::from(EffectItemType::Folder), 16);
1369 }
1370
1371 #[test]
1372 fn effect_item_info_from_raw_returns_none_for_unknown_type() {
1373 assert_eq!(effect_item_info_from_raw("test".to_string(), 999), None);
1374 }
1375
1376 #[test]
1377 fn effect_item_info_from_raw_builds_info_for_known_type() {
1378 assert_eq!(
1379 effect_item_info_from_raw("test".to_string(), 4),
1380 Some(EffectItemInfo {
1381 name: "test".to_string(),
1382 item_type: EffectItemType::Text,
1383 })
1384 );
1385 }
1386
1387 #[test]
1388 fn module_type_try_from_unknown_value_fails() {
1389 assert_eq!(ModuleType::try_from(999), Err(()));
1390 }
1391
1392 #[test]
1393 fn edit_state_try_from_unknown_value_fails() {
1394 assert_eq!(EditState::try_from(999), Err(()));
1395 }
1396}